import React, {
  FC, ChangeEvent, useState, ReactElement, useEffect,
  useRef, KeyboardEvent,
} from 'react';
import classNames from 'classnames';

import Input, { InputProps } from '../Input/input';
import Icon from '../Icon/icon';
import Transition from '../Transition/transition';
import useDebounce from '../../hooks/useDebounce';
import useClickOutside from '../../hooks/useClickOutside';

// 数据源的格式，必须是带有 value 的对象格式
interface DataSourceObject {
  value: string,
}

// 提示列表 数据格式
interface LabelSourceObject {
  key: number | string;
  value: string,
}

// 适应 option 类型为带有 value 字段的对象类型
export type DataSourceType<T = {}> = T & DataSourceObject | undefined;

export type PromptSourceType<T = {}> = T & LabelSourceObject | undefined;

export interface AutoCompleteProps extends Omit<InputProps, 'onSelect'> {
  /**获取提示列表信息 */
  fetchSuggestions: (str: string) => DataSourceType[] | Promise<DataSourceType[]>;
  onSelect?: (item: DataSourceType) => void;

  /**输入数据回调 */
  onInput?: (str: any) => void;

  /**键盘事件回调 */
  onKeyDownCallBack?: (event: KeyboardEvent<HTMLInputElement>) => void;

  /**自定义展示模板 */
  renderOption?: (item: DataSourceType) => ReactElement;
  
  /**提示 label 列表 */
  promptList?: PromptSourceType[];

  /** 文本框的宽度 */
  width?: number,
  ref?: React.Ref<any>;
};

/**
 * 自动完成组件，可根据输入的内容自动补全
 * 
 * ### 引入方式 
 * 
 * ```js 
 * import { AutoComplete } from 'rqx-ui';
 * ```
 */
export const AutoComplete: FC<AutoCompleteProps> = (props) => {
  const {
    value,
    width,
    fetchSuggestions,
    onSelect,
    onInput,
    promptList,
    renderOption,
    onKeyDownCallBack,
    ...rest
  } = props;

  // 文本框的值
  const [inputValue, setInputValue] = useState(value as string);

  // 下拉框中的值 - 数据源
  const [suggestions, setSuggestions] = useState<DataSourceType[]>([]);

  // 下拉框中的原始值
  const [originalList, setOriginalList] = useState<any[]>([]);

  // loading 状态
  const [loading, setLoading] = useState(false);

  // 是否展开 dropDown
  const [ showDropdown, setShowDropdown] = useState(false);

  // 高亮的条目
  const [highlightIndex, setHighlightIndex] = useState(-1);

  // label 提示信息展开图标
  // const [showIcon, setShowIcon] = useState(false);

  // 控制选择数据后重复打开 dropDown
  const triggerSearch = useRef(false);
  const componentRef = useRef<HTMLInputElement>(null);

  // 使用 hooks 组件进行防抖
  const debouncedValue = useDebounce(inputValue, 300);

  useClickOutside(componentRef, () => {
    setShowDropdown(false);
    setSuggestions([]);
  });

  // useEffect(() => {
  //   let widthSum: number = 0;
  //   const childList = document.getElementsByClassName('auto-complete-labels')[0]?.children ?? [];
  //   if(childList.length){
  //     for(let i = 0; i < childList.length; i++) {
  //       widthSum += childList[i]?.clientWidth ?? 0;
  //     }
  //   }
  //   if(widthSum >= (width as number) - 24){
  //     setShowIcon(true);
  //   }
  // }, [width, setShowIcon]);

  useEffect(() => {
    if(debouncedValue && triggerSearch.current){
      setSuggestions([]);
      const results = fetchSuggestions(debouncedValue);
      // 判断是否异步调用
      if (results instanceof Promise) {
        setLoading(true);
        results.then((data) => {
          setLoading(false);
          handleSetList(data);
          if (data.length > 0) {
            setShowDropdown(true);
          }
        });
      } else {
        handleSetList(results);
        setShowDropdown(true)
      }
    } else {
      setShowDropdown(false);
    }
    setHighlightIndex(-1);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [debouncedValue, fetchSuggestions]);

  /**
   * 输入数据的回调
   */
   const handleChangeCallBack = (textValue: any) => {
    if (onInput && typeof onInput === 'function') {
      onInput(textValue);
    }
  };

  /**
   * 匹配到的字符 高亮
   */
  const handleSetList = (dataArr: any[] = []) => {
    const originalArr = dataArr.map((item, index) => {
      return {
        _id: `_${index}`,
        ...item,
      };
    });
    setOriginalList(originalArr);
    const list = originalArr.map((item) => {
      return {
        ...item,
        value:
          item.value && inputValue
            ? item.value
                .toString()
                .replaceAll(inputValue, `<span style='color: #36C2CF;'>${inputValue}</span>`)
            : '',
      };
    });
    setSuggestions(list);
  };

  /**
   * 通过选到的 item 获取原始值
   * @param {record} record
   */
  const getOriginalValue = (record: any) => {
    let rtnObj: DataSourceType = undefined;
    if (originalList.length) {
      originalList.forEach((item) => {
        if (record['_id'] && item['_id'] === record['_id']) {
          rtnObj = item;
        }
      });
    }
    return rtnObj;
  };

  /**
   * 输入值，查询提示项
   * @param e 
   */
  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    const strVal = e?.target?.value?.trim() ?? '';
    setInputValue(strVal);
    handleChangeCallBack(strVal);
    triggerSearch.current = true;
  };

  /**
   * 选择提示项，赋值，并清空列表 隐藏dropdown
   * @param item 
   */
  const handleSelected = (item: DataSourceType) => {
    const record = getOriginalValue(item);
    const inputStr = record && record['value'] ? record['value'] : '';
    setInputValue(inputStr);
    setShowDropdown(false);
    if(onSelect && typeof onSelect === 'function'){
      onSelect(record);
    }
    handleChangeCallBack(inputStr);
    triggerSearch.current = false;
  };

  /**
   * 自定义展示模板
   * @param item 
   * @returns 
   */
  const renderTemp = (item: DataSourceType) => {
    // eslint-disable-next-line react/no-danger
    return renderOption ? (
      renderOption(getOriginalValue(item))
    ) : (
      <div dangerouslySetInnerHTML={{ __html: item?.value ?? '' }} />
    );
  }

  /**
   * 自动补全 dropDown 下拉列表
   * @returns 
   */
  const renderDropdown = () => {
    return (
      <Transition
        in={showDropdown || loading}
        animation="zoom-in-top"
        timeout={300}
        onExited={() => {setSuggestions([])}}
      >
        <ul
          style={{ width: `${width}px` }}
          className="dropdown-area"
        >
          {loading && (
            <div className="suggstions-loading-icon">
              <Icon icon="spinner" spin/>
            </div>
          )}
          {suggestions.map((item, index) => {
            const classes = classNames('dropdown-item', {
              'is-active': index === highlightIndex,
            });
            return (
              <li className={classes} key={index.toString()} onClick={() => handleSelected(item)}>
                {renderTemp(item)}
              </li>
            );
          })}
        </ul>
      </Transition>
    )
  };

  const highlight = (index: number) => {
    if (index < 0) index = 0
    if (index >= suggestions.length) {
      index = suggestions.length - 1
    }
    setHighlightIndex(index)
  }

  const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    switch (e.keyCode) {
      case 13:
        if (suggestions[highlightIndex]) {
          handleSelected(suggestions[highlightIndex]);
        }
        break;
      case 38:
        highlight(highlightIndex - 1);
        break;
      case 40:
        highlight(highlightIndex + 1);
        break;
      case 27:
        setShowDropdown(false);
        break;
      default:
        break;
    }

    if (onKeyDownCallBack && typeof onKeyDownCallBack === 'function') {
      onKeyDownCallBack(e);
    }
  };

  /**
   * 选择提示项
   * @param {*} item
   */
   const handleSetLabel = (item: PromptSourceType) => {
    setInputValue(item?.value ?? '');
    handleChangeCallBack(item);
    if(onSelect && typeof onSelect === 'function'){
      onSelect(item);
    }
  };

  /**
   * 渲染提示信息 列表
   * @param {array} dataList
   * @returns
   */
   const drawSearchLabel = () => {
    const labelList = (promptList || []).map((item, index) => {
      return (
        <span onClick={() => handleSetLabel(item)} style={{ display: 'inline-block' }} key={item?.key ?? index}>
          {item?.value ?? ''}
        </span>
      );
    });

    return labelList;
  };

  return (
    <div className="auto-complete">
      <Input
        ref={componentRef}
        style={{ width: `${width}px` }}
        value={inputValue}
        onChange={handleChange}
        onKeyDown={handleKeyDown}
        {...rest}
      />
      {renderDropdown()}
      {
        promptList && promptList.length ? (
          <>
            <div
              style={{width: `${width}px`}}
              className="auto-complete-labels"
            >
              {drawSearchLabel()}
            </div>
          </>
          
        ) : null
      }
    </div>
  );
};

export default AutoComplete;